﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DNASequencing.core.Algorithms.rating;
using DNASequencing.core.data;
using DNASequencing.core.data.complex;
using DNASequencing.core.Algorithms;
using DNASequencing.core.Algorithms.SeqNegPos;

namespace DNASequencing.core.Algorithms.SeqNeg
{
    /// <summary>
    /// Algorithm for changing sequence from algorithm neg/pos to sequence with only negative errors. 
    /// This algorithm depends on eliminating positive errors by putting not used oligonucleotides to previous result
    /// </summary>
    class NegAlgorithm : AAlgorithm
    {
        //protected List<Oligonucleotide> result;
        //protected List<Oligonucleotide> notUsed;

        public NegAlgorithm(int k, int n, ADataTree dt, IRating rating)
            : base(k, n, dt, rating)
        {
        }

        protected bool addToResult(Oligonucleotide o, List<Oligonucleotide> result)
        {
            int bestError = o.originalSequence.Length - 1; // max by default, if we put this oligonucleotide at the end of the sequence
            int bestPlace = -1;
            int l = o.originalSequence.Length;
            for (int i = 0; i < result.Count - 1; ++i)
            {
                int er1 = l - commonAminoacids(result[i], o) - 1;
                int er2 = l - commonAminoacids(o, result[i + 1]) - 1;
                //System.Console.WriteLine(er1 + " " + er2);
                if (er1 + er2 - result[i + 1].error < bestError)
                {
                    bestError = er1 + er2 - result[i + 1].error;
                    bestPlace = i;
                    System.Console.WriteLine("LOL " + i + ": " + bestError);
                }
            }

            if (bestPlace >= 0)
            {
                o.error = l - commonAminoacids(result[bestPlace], o) - 1;
                result[bestPlace + 1].error = l - commonAminoacids(o, result[bestPlace + 1]) - 1;
                result.Insert(bestPlace + 1, o);
                return true;
            }
            else
            {
                return false;
            }
        }

        protected int commonAminoacids(Oligonucleotide o1, Oligonucleotide o2)
        {
            int l = o1.originalSequence.Length;

            int i = 1;
            for (; i < o1.originalSequence.Length; ++i)
            {
                String s1 = o1.originalSequence.Substring(i);
                String s2 = o2.originalSequence.Substring(0, l - i);

                if (s1 == s2)
                    return l - i;
            }
            return 0;
        }

        private AlgorithmResult negativePart(List<Oligonucleotide> result, List<Oligonucleotide> notUsed)
        {
            bool[] toRemove = new bool[notUsed.Count];
            for (int i = 0; i < notUsed.Count; ++i)
            {
                toRemove[i] = addToResult(notUsed[i], result);
            }
            for (int i = notUsed.Count - 1; i >= 0; --i)
                if (toRemove[i])
                    notUsed.RemoveAt(i);

            return null;
        }

        override public AlgorithmResult process()
        {
            Algorithm alg = new Algorithm(k, n, dt as ComplexDT, new GaussRating(dt.all.Count));
            AlgorithmResult ar = alg.process();


            if (ar.notUsed.Count != 0)
            {
                //listBox.Text += "Sequence with only negative errors: \n";
                //NegAlgorithm nalg = new NegAlgorithm(ar.result, ar.notUsed);
                negativePart(ar.result, ar.notUsed);

                if (ar.notUsed.Count > 1)
                {
                    ComplexDT cdt = new ComplexDT(ar.notUsed);
                    Algorithm alg2 = new Algorithm(k, Int16.MaxValue, cdt, new GaussRating(cdt.all.Count));
                    AlgorithmResult ar2 = alg2.process();
                    best = SequenceUtils.mergeSequences(ar.result, ar2.result);
                }
                else if (ar.notUsed.Count == 1)
                {
                    ar.notUsed[0].error = 9;
                    ar.result.Add(ar.notUsed[0]);
                    //StrictLogger.printSequence(ar.result);
                    best = ar.result;
                }
                else
                    best = ar.result;

                return new AlgorithmResult(best, null, negativeError, positiveError);
                //return 
                //listBox.Text += SequenceUtils.sequenceListToString(result) + "\n";
                //listBox.Text += "Negative error: " + SequenceUtils.extractNegativeError(result) + "\n";
            }
            else
                return ar;
        }
    }
}
